package com.raorao.rpc.server;

import com.alibaba.fastjson.JSON;
import com.raorao.rpc.common.constants.ConfigConstant;
import com.raorao.rpc.common.serializer.ZookeeperSerializer;
import com.raorao.rpc.common.utils.BeanPropertyUtils;
import com.raorao.rpc.common.utils.PathUtil;
import com.raorao.rpc.properties.ConfigProperty;
import com.raorao.rpc.server.model.ConfigModel;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Zookeeper文件服务
 *
 * @author raorao
 * @since 1.0.0
 */
public class ZookeeperConfigFileService implements ConfigFileService {

    private static Logger logger = LoggerFactory.getLogger(ZookeeperConfigFileService.class);

    private ConfigProperty configProperty;

    /**
     * zookeeper客户端
     */
    private ZkClient zkClient;

    /**
     * 配置文件清单map
     */
    private Map<String,List<String>> fileConfigMap=new HashMap<>();

    public ZookeeperConfigFileService(ConfigProperty configProperty){
        this.configProperty=configProperty;
        //1、创建zk客户端
        zkClient = new ZkClient(configProperty.getAddress());
        zkClient.setZkSerializer(new ZookeeperSerializer());
        logger.debug("【raorao-config】zkClient初始化，Address="+configProperty.getAddress());
    }

    @Override
    public Map<String, ConfigModel> getConfigFile(String fileName) {
        logger.debug("【raorao-config】zkClient加载配置文件："+fileName);
        Map<String, ConfigModel> result=new HashMap<>();
        //1、拼接路径
        StringBuilder path=new StringBuilder();
        path.append(ConfigConstant.ZK_SERVICE_PATH).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getProject()).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getEnvironment()).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getVersion()).append(ConfigConstant.PATH_DELIMITER);
        path.append(ConfigConstant.PATH_INFO_PATH).append(ConfigConstant.PATH_DELIMITER);
        path.append(fileName);
        String basePath=path.toString();
        List<String> children=new ArrayList<>();
        //2、获取配置
        if(zkClient.exists(basePath)){
            children=zkClient.getChildren(basePath);
        }
        for (String item:children){
            String childrenPath=basePath+ConfigConstant.PATH_DELIMITER+item;
            String val = zkClient.readData(childrenPath);
            try{
                ConfigModel configModel = JSON.parseObject(val,ConfigModel.class);
                result.put(item,configModel);
            }catch (Exception ex){
                logger.error("【raorao-config】zookeeper客户端获取{}节点数据失败",childrenPath);
            }
        }
        return result;
    }

    @Override
    public void saveConfigFile(String fileName,String data) {
        String path = PathUtil.getUserDir();
        String filepath=path+ConfigConstant.DEFAULT_FILE_PATH_DELIMITER+fileName+ConfigConstant.DEFAULT_FILE_SUFFIX;
        try (FileWriter fileWriter = new FileWriter(filepath)) {
            fileWriter.append(data);
            logger.debug("【raorao-config】下载配置文件{}至本地，path={}",fileName,filepath);
        }catch (Exception ex){
            logger.error("【raorao-config】下载配置文件{}至本地失败！！！",fileName);
        }
    }

    @Override
    public void watchConfig(String fileName, Object bean) {
        //1、拼接路径
        StringBuilder path=new StringBuilder();
        path.append(ConfigConstant.ZK_SERVICE_PATH).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getProject()).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getEnvironment()).append(ConfigConstant.PATH_DELIMITER);
        path.append(this.configProperty.getVersion()).append(ConfigConstant.PATH_DELIMITER);
        path.append(ConfigConstant.PATH_INFO_PATH).append(ConfigConstant.PATH_DELIMITER);
        path.append(fileName);
        if(!zkClient.exists(path.toString()))return;
        List<String> childrens=zkClient.getChildren(path.toString());
        fileConfigMap.put(fileName,childrens);
        // 2、对配置文件节点注册监听器
        zkClient.subscribeChildChanges(path.toString(), new IZkChildListener() {
            // 当子节点发生改变时，调用此方法
            @Override
            public void handleChildChange(String path, List<String> childList) throws Exception {
                logger.debug("【raorao-config】节点 " + path + "子节点更新：" + childList);
                String[] split = path.split(ConfigConstant.PATH_DELIMITER);
                String fileName=split[split.length-1];
                if(fileConfigMap.containsKey(fileName)){
                    List<String> childrens = fileConfigMap.get(fileName);
                    if(childList==null){
                        childrens=new ArrayList<>();
                    }else {
                        for(String item:childList){
                            if(!childrens.contains(item)){}{
                                childrens.add(item);
                                dataListener(path+ConfigConstant.PATH_DELIMITER+item,bean);
                            }
                        }
                    }
                    fileConfigMap.put(fileName,childrens);
                }
            }
        });
        //3、对数据注册监听器
        for (String children:childrens){
            this.dataListener(path.toString()+ConfigConstant.PATH_DELIMITER+children,bean);
        }
    }

    /**
     * @description: 节点数据变更
     * @param: path
     * @param: obj
     * @return: void
     * @author raorao
     * @date: 2021/12/10 15:43
     */
    private void dataListener(String path,Object obj){
        zkClient.subscribeDataChanges(path, new IZkDataListener() {
            // 节点数据改变后执行此逻辑
            @Override
            public void handleDataChange(String path, Object data) throws Exception {
                logger.debug("【raorao-config】节点 "+ path + " 数据更新：" + data);
                String[] split = path.split(ConfigConstant.PATH_DELIMITER);
                String fileName=split[split.length-2];
                Map<String, ConfigModel> configFile = getConfigFile(fileName);
                BeanPropertyUtils.changeBeanProperty(configFile,obj);
            }

            // 节点数据删除后执行此逻辑
            @Override
            public void handleDataDeleted(String path) throws Exception {
                logger.debug("【raorao-config】节点数据已删除 "+ path);
                String[] split = path.split(ConfigConstant.PATH_DELIMITER);
                String fileName=split[split.length-2];
                Map<String, ConfigModel> configFile = getConfigFile(fileName);
                BeanPropertyUtils.changeBeanProperty(configFile,obj);
            }
        });
    }
}
